package dashboard

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"

	"github.com/golang/glog"
	"github.com/gorilla/mux"

	apiNode "eccgateway/pkg/api/node"
	"eccgateway/pkg/config"
	"eccgateway/pkg/util"
)

const (
	HttpProtocol           = "http"
	OriginHostReqHeader    = "X-Origin-Host"
	ForwardedHostReqHeader = "X-Forwarded-Host"
)

var dashboardAddress string

type DashboardController struct{}

func (c *DashboardController) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	trueUri := "/" + mux.Vars(r)["rest"]

	if len(dashboardAddress) == 0 {
		dashboardAddress = config.GetConfig().String("dashboardAddress")
		glog.Infof("dashboardAddress is %v", dashboardAddress)
	}

	origin, err := url.Parse(dashboardAddress)
	if err != nil {
		glog.Errorf("parse proxy backend address failed, address: %v, err: %v", dashboardAddress, err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, fmt.Sprintf("parse proxy backend address failed, %v", err))
		return
	}

	glog.Infof("this request will send to address: %v, uri: %v, method: %v", dashboardAddress, trueUri, r.Method)
	director := func(req *http.Request) {
		req.Header.Add(ForwardedHostReqHeader, req.Host)
		req.Header.Add(OriginHostReqHeader, origin.Host)
		req.URL.Scheme = HttpProtocol
		req.URL.Host = origin.Host
		req.URL.Path = trueUri
	}
	proxy := &httputil.ReverseProxy{Director: director}
	proxy.ServeHTTP(w, r)
}

const (
	labelSelector = "labelSelector"

	nodeURLInDashboard = "%s/api/%s"
)

func (c DashboardController) GetNodesBySelfFilter(w http.ResponseWriter, r *http.Request) {
	if len(dashboardAddress) == 0 {
		dashboardAddress = config.GetConfig().String("dashboardAddress")
		glog.Infof("dashboardAddress is %v", dashboardAddress)
	}

	// send request to dashboardAddress
	url := fmt.Sprintf(nodeURLInDashboard, dashboardAddress, r.RequestURI)
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		glog.Errorf("construct request for dashboard node failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusInternalServerError, fmt.Sprintf("%v", err))
		return
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		glog.Errorf("send request to dashboard failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusInternalServerError, fmt.Sprintf("%v", err))
		return
	}
	if resp.StatusCode != http.StatusOK {
		glog.Errorf("dashboard response status code: %v", resp.StatusCode)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusInternalServerError, fmt.Sprintf("dashboard response status code: %v", resp.StatusCode))
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		glog.Errorf("read dashboard resp body failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusInternalServerError, fmt.Sprintf("%v", err))
		return
	}

	query := r.URL.Query()
	if query[labelSelector] == nil && len(query[labelSelector]) < 1 {
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write(body)
		return
	}

	// filter node by label
	var nodelist apiNode.NodeList
	err = json.Unmarshal(body, &nodelist)
	if err != nil {
		glog.Errorf("unmarshal dashboard resp body failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusInternalServerError, fmt.Sprintf("%v", err))
		return
	}

	strs := strings.Split(query[labelSelector][0], "=")
	if len(strs) != 2 {
		glog.Infof("query[labelSelector][0] format is invalid, query[labelSelector][0]: %v", query[labelSelector][0])
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write(body)
		return
	}
	filteredNodes := make([]apiNode.Node, 0, nodelist.ListMeta.TotalItems)
	for _, node := range nodelist.Nodes {
		for k, v := range node.ObjectMeta.Labels {
			if k == strs[0] && v == strs[1] {
				filteredNodes = append(filteredNodes, node)
				break
			}
		}
	}
	nodelist.ListMeta.TotalItems = len(filteredNodes)
	nodelist.Nodes = filteredNodes

	body, err = json.Marshal(nodelist)
	if err != nil {
		glog.Errorf("Marshal node list failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusInternalServerError, fmt.Sprintf("%v", err))
		return
	}

	w.WriteHeader(http.StatusOK)
	_, _ = w.Write(body)
}
